Creating new pipeline using seurat v4.0.2 available 2021.06.23
Important notes:

Setup

Load libraries required for Seuratv4

knitr::opts_knit$set(root.dir = "~/Desktop/10XGenomicsData/msAggr_scRNASeq/IndividualPops/")
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(Seurat)
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
Attaching SeuratObject
library(patchwork)
library(ggplot2)
library(clustree)
Loading required package: ggraph

Source functions

source("~/Desktop/10XGenomicsData/msAggr_scRNASeq/RFunctions/read_10XGenomics_data.R")
source("~/Desktop/10XGenomicsData/msAggr_scRNASeq/RFunctions/PercentVariance.R")
source("~/Desktop/10XGenomicsData/msAggr_scRNASeq/RFunctions/ColorPalette.R")
source("~/Desktop/10XGenomicsData/msAggr_scRNASeq/RFunctions/Mouse2Human_idconversion.R")

A note about using SCTransform versus ScaleData

https://bioconductor.org/packages/3.10/workflows/vignettes/simpleSingleCell/inst/doc/batch.html#62_for_gene-based_analyses >You can also normalize and scale data for the RNA assay. There are numerous resources on this, but Aaron Lun describes why the original log-normalized values should be used for DE and visualizations of expression quite well here: > >For gene-based procedures like differential expression (DE) analyses or gene network construction, it is desirable to use the original log-expression values or counts. The corrected values are only used to obtain cell-level results such as clusters or trajectories. Batch effects are handled explicitly using blocking terms or via a meta-analysis across batches. We do not use the corrected values directly in gene-based analyses, for various reasons: > >It is usually inappropriate to perform DE analyses on batch-corrected values, due to the failure to model the uncertainty of the correction. This usually results in loss of type I error control, i.e., more false positives than expected. > >The correction does not preserve the mean-variance relationship. Applications of common DE methods like edgeR or limma are unlikely to be valid. > >Batch correction may (correctly) remove biological differences between batches in the course of mapping all cells onto a common coordinate system. Returning to the uncorrected expression values provides an opportunity for detecting such differences if they are of interest. Conversely, if the batch correction made a mistake, the use of the uncorrected expression values provides an important sanity check. > >In addition, the normalized values in SCT and integrated assays don’t necessary correspond to per-gene expression values anyway, rather containing residuals (in the case of the scale.data slot for each).

Mess with how to load 4 cell populations into single seurat object

SET SEED?????!!!!!

Set global variables

projectName <- "CMPSubpop"
jackstraw.dim <- 40

Store session info

sessionInfo.filename <- paste0(projectName, "_sessionInfo.txt")
sink(sessionInfo.filename)
sessionInfo()
sink()
setwd("~/Desktop/10XGenomicsData/cellRanger/") # temporarily changing wd only works if you run the entire chunk at once
data_file.list <- read_10XGenomics_data(sample.list = c("CMPm2"))
data.object<-Read10X(data_file.list)
seurat.object<- CreateSeuratObject(counts = data.object, min.cells = 3, min.genes = 200, project = projectName)

Clean up to free memory

remove(data.object)

Add mitochondrial metadata and plot some basic features

seurat.object[["percent.mt"]] <- PercentageFeatureSet(seurat.object, pattern = "^mt-")
VlnPlot(seurat.object, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3, pt.size = 0, fill.by = 'orig.ident', )

plot1 <- FeatureScatter(seurat.object, feature1 = "nCount_RNA", feature2 = "percent.mt", group.by = "orig.ident", pt.size = 0.01)
plot2 <- FeatureScatter(seurat.object, feature1 = "nCount_RNA", feature2 = "nFeature_RNA", group.by = "orig.ident", pt.size = 0.01)
plot1 + plot2

remove low quality cells require: nFeature_RNA between 200 and 4000 (inclusive) require: percent.mt <= 5

print(paste("original object:", nrow(seurat.object@meta.data), "cells", sep = " "))
[1] "original object: 12540 cells"
seurat.object <- subset(seurat.object, 
                                                subset = nFeature_RNA >=200 & 
                                                    nFeature_RNA <= 4000 & 
                                                    percent.mt <= 5
)
print(paste("new object:", nrow(seurat.object@meta.data), "cells", sep = " "))
[1] "new object: 12059 cells"

Normalization

Struggling to wrap my head around this one. It seems that SCTransform is best for batch correction, but NormalizeData and ScaleData are best for DGE. Several vignettes have performed both

`selection.method
How to choose top variable features. Choose one of :

vst: First, fits a line to the relationship of log(variance) and log(mean) using local polynomial regression (loess). Then standardizes the feature values using the observed mean and expected variance (given by the fitted line). Feature variance is then calculated on the standardized values after clipping to a maximum (see clip.max parameter).

mean.var.plot (mvp): First, uses a function to calculate average expression (mean.function) and dispersion (dispersion.function) for each feature. Next, divides features into num.bin (deafult 20) bins based on their average expression, and calculates z-scores for dispersion within each bin. The purpose of this is to identify variable features while controlling for the strong relationship between variability and average expression.

dispersion (disp): selects the genes with the highest dispersion values`

seurat.object <- NormalizeData(seurat.object, normalization.method = "LogNormalize", scale.factor = 10000)
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|

Find variable features

seurat.object <- FindVariableFeatures(seurat.object, selection.method = "vst", nfeatures = 2000)
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
top10 <- head(VariableFeatures(seurat.object), 10)
plot1 <- VariableFeaturePlot(seurat.object)
plot2 <- LabelPoints(plot = plot1, points = top10, repel = TRUE)
When using repel, set xnudge and ynudge to 0 for optimal results
plot1 + plot2

Scale data (linear transformation)

all.genes <- rownames(seurat.object)
seurat.object <- ScaleData(seurat.object, features = all.genes, vars.to.regress = c("nCount_RNA", "nFeature_RNA"))
Regressing out nCount_RNA, nFeature_RNA

  |                                                                                                                               
  |                                                                                                                         |   0%
  |                                                                                                                               
  |=                                                                                                                        |   0%
  |                                                                                                                               
  |=                                                                                                                        |   1%
  |                                                                                                                               
  |==                                                                                                                       |   1%
  |                                                                                                                               
  |==                                                                                                                       |   2%
  |                                                                                                                               
  |===                                                                                                                      |   2%
  |                                                                                                                               
  |===                                                                                                                      |   3%
  |                                                                                                                               
  |====                                                                                                                     |   3%
  |                                                                                                                               
  |====                                                                                                                     |   4%
  |                                                                                                                               
  |=====                                                                                                                    |   4%
  |                                                                                                                               
  |=====                                                                                                                    |   5%
  |                                                                                                                               
  |======                                                                                                                   |   5%
  |                                                                                                                               
  |=======                                                                                                                  |   5%
  |                                                                                                                               
  |=======                                                                                                                  |   6%
  |                                                                                                                               
  |========                                                                                                                 |   6%
  |                                                                                                                               
  |========                                                                                                                 |   7%
  |                                                                                                                               
  |=========                                                                                                                |   7%
  |                                                                                                                               
  |=========                                                                                                                |   8%
  |                                                                                                                               
  |==========                                                                                                               |   8%
  |                                                                                                                               
  |==========                                                                                                               |   9%
  |                                                                                                                               
  |===========                                                                                                              |   9%
  |                                                                                                                               
  |===========                                                                                                              |  10%
  |                                                                                                                               
  |============                                                                                                             |  10%
  |                                                                                                                               
  |=============                                                                                                            |  10%
  |                                                                                                                               
  |=============                                                                                                            |  11%
  |                                                                                                                               
  |==============                                                                                                           |  11%
  |                                                                                                                               
  |==============                                                                                                           |  12%
  |                                                                                                                               
  |===============                                                                                                          |  12%
  |                                                                                                                               
  |===============                                                                                                          |  13%
  |                                                                                                                               
  |================                                                                                                         |  13%
  |                                                                                                                               
  |================                                                                                                         |  14%
  |                                                                                                                               
  |=================                                                                                                        |  14%
  |                                                                                                                               
  |==================                                                                                                       |  14%
  |                                                                                                                               
  |==================                                                                                                       |  15%
  |                                                                                                                               
  |===================                                                                                                      |  15%
  |                                                                                                                               
  |===================                                                                                                      |  16%
  |                                                                                                                               
  |====================                                                                                                     |  16%
  |                                                                                                                               
  |====================                                                                                                     |  17%
  |                                                                                                                               
  |=====================                                                                                                    |  17%
  |                                                                                                                               
  |=====================                                                                                                    |  18%
  |                                                                                                                               
  |======================                                                                                                   |  18%
  |                                                                                                                               
  |======================                                                                                                   |  19%
  |                                                                                                                               
  |=======================                                                                                                  |  19%
  |                                                                                                                               
  |========================                                                                                                 |  19%
  |                                                                                                                               
  |========================                                                                                                 |  20%
  |                                                                                                                               
  |=========================                                                                                                |  20%
  |                                                                                                                               
  |=========================                                                                                                |  21%
  |                                                                                                                               
  |==========================                                                                                               |  21%
  |                                                                                                                               
  |==========================                                                                                               |  22%
  |                                                                                                                               
  |===========================                                                                                              |  22%
  |                                                                                                                               
  |===========================                                                                                              |  23%
  |                                                                                                                               
  |============================                                                                                             |  23%
  |                                                                                                                               
  |============================                                                                                             |  24%
  |                                                                                                                               
  |=============================                                                                                            |  24%
  |                                                                                                                               
  |==============================                                                                                           |  24%
  |                                                                                                                               
  |==============================                                                                                           |  25%
  |                                                                                                                               
  |===============================                                                                                          |  25%
  |                                                                                                                               
  |===============================                                                                                          |  26%
  |                                                                                                                               
  |================================                                                                                         |  26%
  |                                                                                                                               
  |================================                                                                                         |  27%
  |                                                                                                                               
  |=================================                                                                                        |  27%
  |                                                                                                                               
  |=================================                                                                                        |  28%
  |                                                                                                                               
  |==================================                                                                                       |  28%
  |                                                                                                                               
  |==================================                                                                                       |  29%
  |                                                                                                                               
  |===================================                                                                                      |  29%
  |                                                                                                                               
  |====================================                                                                                     |  29%
  |                                                                                                                               
  |====================================                                                                                     |  30%
  |                                                                                                                               
  |=====================================                                                                                    |  30%
  |                                                                                                                               
  |=====================================                                                                                    |  31%
  |                                                                                                                               
  |======================================                                                                                   |  31%
  |                                                                                                                               
  |======================================                                                                                   |  32%
  |                                                                                                                               
  |=======================================                                                                                  |  32%
  |                                                                                                                               
  |=======================================                                                                                  |  33%
  |                                                                                                                               
  |========================================                                                                                 |  33%
  |                                                                                                                               
  |=========================================                                                                                |  33%
  |                                                                                                                               
  |=========================================                                                                                |  34%
  |                                                                                                                               
  |==========================================                                                                               |  34%

Save progress

# save.image(file = paste0(projectName, '.RData'))
saveRDS(seurat.object, file = paste0(projectName, "_raw.RDS"))

PCA

linear dimensional reduction. Default are based on VariableFeatures, but can be changed

seurat.object <- RunPCA(seurat.object, features = VariableFeatures(object = seurat.object))
PC_ 1 
Positive:  Vamp5, Nkg7, Car2, Apoe, Ctla2a, Gata2, Cd63, Rps23, Rps2, Itga2b 
       Adgrg1, Sdsl, Nrgn, Hmgb3, Pdcd4, Pf4, F2r, Angpt1, Hsp90ab1, Rps17 
       H2-Q7, Gp5, Aqp1, Cavin2, Fyb, Tmem40, Icam4, Mfsd2b, Ifitm3, Lat 
Negative:  Lgals3, Aif1, Id2, Cst3, H2-Aa, Cd74, Plbd1, Irf8, H2-Eb1, Ctsh 
       Ccr2, Ifi205, Tmsb10, Batf3, Psap, Cd52, Ms4a6c, Lsp1, H2-Ab1, S100a6 
       Pld4, Naaa, Ctss, Tyrobp, Rab7b, Ckb, Ifi30, Mpeg1, H2-DMb1, Ly86 
PC_ 2 
Positive:  Prtn3, Ctsg, H2afy, Mpo, Emb, Ccl9, Plac8, Cd34, BC035044, Pgam1 
       Ramp1, Mif, Fkbp1a, Phgdh, Serpinb1a, Pkm, Bex6, Cd53, Tyrobp, Ung 
       Sell, Cdca7, Nme2, Calr, Hsp90ab1, Serpinf1, Anxa3, Limd2, Rps23, Clec12a 
Negative:  Ube2c, Cenpf, Nusap1, Mki67, Cenpa, Hmmr, Birc5, Prc1, Ckap2l, Cdca8 
       Ccnb1, H2afx, Cks2, Plk1, Cd9, Apoe, Tpx2, Cdc20, Top2a, Ccnb2 
       Vamp5, Cenpe, Nucks1, Kif22, Mfsd2b, Lockd, Hist1h2ap, Pf4, Cavin2, Cdkn2d 
PC_ 3 
Positive:  Pf4, Tmsb4x, Cavin2, Serpine2, Rap1b, Treml1, Vwf, Rab27b, F2rl2, Pbx1 
       Gucy1a1, Cd9, Ehd3, Gp1bb, Itga2b, Slc14a1, Mmrn1, Gp9, Timp3, Plek 
       Nrgn, Unc119, Rgs10, Gm10419, Ptgs1, Mpl, Ache, Trpc6, Lims1, Ppbp 
Negative:  Plac8, Cks2, Cenpa, Ube2c, Tubb4b, Arl6ip1, Hmgb2, Hmmr, Sox4, Ccnb2 
       Tubb5, Cdc20, Nusap1, Ccnb1, Cenpf, Fos, Ramp1, Cdca8, Hist1h2ap, Stmn1 
       Hist1h2ac, Vim, Tent5a, Birc5, Aurka, Cenpe, Tpx2, Cdca3, Prc1, Cpa3 
PC_ 4 
Positive:  Csrp3, Car1, Jun, Apoe, Jund, Ifngr1, Rnase6, Fos, Vim, Egr1 
       Ifitm1, Klf1, Tspo2, H2-Eb1, Ier2, Vamp5, Crip1, Cd74, Dusp1, Junb 
       Gstm5, H2-Aa, Plbd1, Itgb7, Gm15915, Id2, H2-Ab1, Lgals3, Aqp1, Blvrb 
Negative:  H2afz, Hmgn2, Birc5, Hmgb1, Tuba1b, Top2a, Hmgb2, Cdca8, Mki67, Pclaf 
       Cks1b, Spc24, Ppia, Mif, Pbk, Cdk1, Ckap2l, Lockd, Ran, Cdca3 
       Smc4, Hist1h1b, Nusap1, Pf4, Stmn1, Cenpf, Slpi, Cavin2, Hmmr, Smc2 
PC_ 5 
Positive:  Pclaf, Tyms, Rrm2, Tk1, Pcna, Dut, Lig1, Ranbp1, Stmn1, Tuba1b 
       Gmnn, Hsp90aa1, Ptma, Slbp, Tipin, Dctpp1, Clspn, Dhfr, Rad51, Fen1 
       Spc24, Hist1h1b, Car1, Rad51ap1, Dtymk, Cycs, Rrm1, Uhrf1, Dek, Syce2 
Negative:  Hist1h2bc, Tsc22d1, Smim14, Ccnb2, Serpinb1a, Ccl9, Cd27, Cd34, Tmsb4x, Cenpa 
       Cdc20, Cdkn3, Prtn3, Gpr171, Gm19590, Glipr1, Satb1, Plek, Ccnb1, Ube2c 
       Rgs2, Lims1, Cenpf, Cd9, Ccl3, Bex6, Dhrs3, Hist1h1c, Ifi203, Mef2c 

Plot results

VizDimLoadings(seurat.object, dims = 1:6, nfeatures = 10, reduction = "pca", ncol = 2)

DimPlot colored by orig.ident

DimPlot(seurat.object, reduction = "pca", group.by = "orig.ident")

Let’s put in a concerted effort to pick the right dimensionality using the newest software

# jackstraw.dim <- 40
# seurat.object <- JackStraw(seurat.object, num.replicate = 100, dims = jackstraw.dim) #runs ~50 min
# seurat.object <- ScoreJackStraw(seurat.object, dims = 1:jackstraw.dim)
# save.image(paste0(projectName, ".RData"))

Draw dim.reduction plots

# JackStrawPlot(seurat.object, dims = 25:36)
ElbowPlot(seurat.object, ndims = 50)
percent.variance(seurat.object@reductions$pca@stdev)

Number of PCs describing X% of variance

ElbowPlot(seurat.object, ndims = 50)

percent.variance(seurat.object@reductions$pca@stdev)

Percent of PCs describing X% of variance (transcribed from above cell because I don’t know how to freeze results)

Num pcs for 80% variance: 12 Num pcs for 85% variance: 18 Num pcs for 90% variance: 26 Num pcs for 95% variance: 37

ID clusters based on different variances

Describe 80% of variance with 12 dimensions

Neighborhood and umap

set total.var <- 80%

tot.var <- percent.variance(seurat.object@reductions$pca@stdev, plot.var = FALSE, return.val = TRUE)
paste0("Num pcs for 80% variance: ", length(which(cumsum(tot.var) <= 80)))
[1] "Num pcs for 80% variance: 18"
paste0("Num pcs for 85% variance: ", length(which(cumsum(tot.var) <= 85)))
[1] "Num pcs for 85% variance: 25"
paste0("Num pcs for 90% variance: ", length(which(cumsum(tot.var) <= 90)))
[1] "Num pcs for 90% variance: 33"
paste0("Num pcs for 95% variance: ", length(which(cumsum(tot.var) <= 95)))
[1] "Num pcs for 95% variance: 41"

Plot UMAP

tot.var <- percent.variance(seurat.object@reductions$pca@stdev, plot.var = FALSE, return.val = TRUE)
ndims <- length(which(cumsum(tot.var) <= 80))

seurat.object <- FindNeighbors(seurat.object, dims = 1:ndims)
Computing nearest neighbor graph
Computing SNN
seurat.object <- FindClusters(seurat.object, resolution = 0.5)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 401749

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8537
Number of communities: 11
Elapsed time: 3 seconds
seurat.object <- RunUMAP(seurat.object, dims = 1: ndims)
Warning: The default method for RunUMAP has changed from calling Python UMAP via reticulate to the R-native UWOT using the cosine metric
To use Python UMAP via reticulate, set umap.method to 'umap-learn' and metric to 'correlation'
This message will be shown once per session
18:16:37 UMAP embedding parameters a = 0.9922 b = 1.112
18:16:37 Read 12059 rows and found 18 numeric columns
18:16:37 Using Annoy for neighbor search, n_neighbors = 30
18:16:37 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
18:16:39 Writing NN index file to temp file /var/folders/4f/fwrj6fnn1dn4g8wsf0zv563hjsvl24/T//RtmplnWyat/file1481828295e68
18:16:39 Searching Annoy index using 1 thread, search_k = 3000
18:16:46 Annoy recall = 100%
18:16:46 Commencing smooth kNN distance calibration using 1 thread
18:16:48 Initializing from normalized Laplacian + noise
18:16:49 Commencing optimization for 200 epochs, with 488156 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
18:16:59 Optimization finished
for(x in c(0.5, 1, 1.5, 2, 2.5)){
    seurat.object <- FindClusters(seurat.object, resolution = x)
}
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 401749

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8537
Number of communities: 11
Elapsed time: 3 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 401749

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8051
Number of communities: 18
Elapsed time: 3 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 401749

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.7743
Number of communities: 24
Elapsed time: 3 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 401749

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.7514
Number of communities: 29
Elapsed time: 2 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 401749

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.7321
Number of communities: 34
Elapsed time: 2 seconds
for (meta.col in colnames(seurat.object@meta.data)){
    if(grepl(pattern = ("RNA_snn_res"), x = meta.col)==TRUE){
        myplot <- DimPlot(seurat.object, 
                                            group.by = meta.col,
                                            reduction = "umap", 
                                            cols = color.palette
        ) + 
            ggtitle(paste0(projectName, " dim", ndims, "res", gsub("RNA_snn_res", "", meta.col) ))
        plot(myplot)
    }
}

Evaluate cluster stability

saveRDS(seurat.object, file = paste0(projectName, "_dim", ndims, ".RDS"))

Describe 85% of variance

Neighborhood and umap

set total.var <- 85%

clustree(seurat.object, prefix = "RNA_snn_res.", node_colour = "sc3_stability") + 
    scale_color_continuous(low = 'red3', high = 'white')
Warning: The `add` argument of `group_by()` is deprecated as of dplyr 1.0.0.
Please use the `.add` argument instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_warnings()` to see where this warning was generated.

Plot UMAP

tot.var <- percent.variance(seurat.object@reductions$pca@stdev, plot.var = FALSE, return.val = TRUE)
ndims <- length(which(cumsum(tot.var) <= 85))

seurat.object <- FindNeighbors(seurat.object, dims = 1:ndims)
Computing nearest neighbor graph
Computing SNN
seurat.object <- FindClusters(seurat.object, resolution = 0.5)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 424091

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8508
Number of communities: 9
Elapsed time: 3 seconds
seurat.object <- RunUMAP(seurat.object, dims = 1: ndims)
18:20:36 UMAP embedding parameters a = 0.9922 b = 1.112
18:20:36 Read 12059 rows and found 25 numeric columns
18:20:36 Using Annoy for neighbor search, n_neighbors = 30
18:20:36 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
18:20:38 Writing NN index file to temp file /var/folders/4f/fwrj6fnn1dn4g8wsf0zv563hjsvl24/T//RtmplnWyat/file1481833f51271
18:20:38 Searching Annoy index using 1 thread, search_k = 3000
18:20:45 Annoy recall = 100%
18:20:46 Commencing smooth kNN distance calibration using 1 thread
18:20:48 Initializing from normalized Laplacian + noise
18:20:48 Commencing optimization for 200 epochs, with 500658 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
18:20:59 Optimization finished
for(x in c(0.5, 1, 1.5, 2, 2.5)){
    seurat.object <- FindClusters(seurat.object, resolution = x)
}
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 424091

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8508
Number of communities: 9
Elapsed time: 3 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 424091

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8026
Number of communities: 18
Elapsed time: 3 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 424091

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.7700
Number of communities: 22
Elapsed time: 3 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 424091

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.7461
Number of communities: 28
Elapsed time: 3 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 424091

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.7257
Number of communities: 33
Elapsed time: 3 seconds
for (meta.col in colnames(seurat.object@meta.data)){
    if(grepl(pattern = ("RNA_snn_res"), x = meta.col)==TRUE){
        myplot <- DimPlot(seurat.object, 
                                            group.by = meta.col,
                                            reduction = "umap", 
                                            cols = color.palette
        ) + 
            ggtitle(paste0(projectName, " dim", ndims, "res", gsub("RNA_snn_res", "", meta.col) ))
        plot(myplot)
    }
}

Evaluate cluster stability

Must ensure we have the right cluster stability, that is, cells that start in the same cluster tend to stay in the same cluster. If your data is over-clustered, cells will bounce between groups.

Following [this tutorial by Matt O.].https://towardsdatascience.com/10-tips-for-choosing-the-optimal-number-of-clusters-277e93d72d92. Previously my favourite has been Clustree, which gives a nice visual NB: For some reason clustree::clustree() didn’t work, whereas library(clustree) followed by clustree() did.

saveRDS(seurat.object, file = paste0(projectName, "_dim", ndims, ".RDS"))

Describe 90% of variance

Neighborhood and umap

clustree(seurat.object, prefix = "RNA_snn_res.", node_colour = "sc3_stability") + 
    scale_color_continuous(low = 'red3', high = 'white')

Plot UMAP

tot.var <- percent.variance(seurat.object@reductions$pca@stdev, plot.var = FALSE, return.val = TRUE)
ndims <- length(which(cumsum(tot.var) <= 90))

seurat.object <- FindNeighbors(seurat.object, dims = 1:ndims)
Computing nearest neighbor graph
Computing SNN
seurat.object <- FindClusters(seurat.object, resolution = 0.5)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 452999

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8502
Number of communities: 9
Elapsed time: 3 seconds
seurat.object <- RunUMAP(seurat.object, dims = 1: ndims)
18:24:56 UMAP embedding parameters a = 0.9922 b = 1.112
18:24:56 Read 12059 rows and found 33 numeric columns
18:24:56 Using Annoy for neighbor search, n_neighbors = 30
18:24:56 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
18:24:58 Writing NN index file to temp file /var/folders/4f/fwrj6fnn1dn4g8wsf0zv563hjsvl24/T//RtmplnWyat/file148181e8dd35d
18:24:58 Searching Annoy index using 1 thread, search_k = 3000
18:25:05 Annoy recall = 100%
18:25:06 Commencing smooth kNN distance calibration using 1 thread
18:25:08 Initializing from normalized Laplacian + noise
18:25:09 Commencing optimization for 200 epochs, with 513294 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
18:25:20 Optimization finished
for(x in c(0.5, 1, 1.5, 2, 2.5)){
    seurat.object <- FindClusters(seurat.object, resolution = x)
}
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 452999

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8502
Number of communities: 9
Elapsed time: 3 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 452999

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.7974
Number of communities: 17
Elapsed time: 4 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 452999

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.7652
Number of communities: 22
Elapsed time: 3 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 452999

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.7404
Number of communities: 27
Elapsed time: 4 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 452999

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.7189
Number of communities: 30
Elapsed time: 3 seconds
for (meta.col in colnames(seurat.object@meta.data)){
    if(grepl(pattern = "RNA_snn_res", x = meta.col)==TRUE | grepl(pattern = "orig.ident", x = meta.col)==TRUE){
        myplot <- DimPlot(seurat.object, 
                                            group.by = meta.col,
                                            reduction = "umap", 
                                            pt.size = 1,
                                            cols = color.palette) + 
            ggtitle(paste0(projectName, " dim", ndims, "res.", gsub("RNA_snn_res.", "", meta.col) ))
        plot(myplot)
        png(filename = paste0(projectName, " dim", ndims, "res.", gsub("RNA_snn_res.", "", meta.col), "-umap.png"), height = 800, width = 800)
        plot(myplot)
        dev.off()
        myplot <- DimPlot(seurat.object, 
                                            group.by = meta.col,
                                            reduction = "umap", 
                                            pt.size = 1,
                                            cols = color.palette) + 
            facet_wrap(meta.col) + 
            ggtitle(paste0(projectName, " dim", ndims, "res.", gsub("RNA_snn_res.", "", meta.col)))
        
        png(filename = paste0(projectName, " dim", ndims, "res.", gsub("RNA_snn_res.", "", meta.col), "-umap_FacetRes.png"), height = 800, width = 800)
        plot(myplot)
        dev.off()
        
    }
}

Evaluate cluster stability

saveRDS(seurat.object, file = paste0(projectName, "_dim", ndims, ".RDS"))

Describe 95% of variance

Neighborhood and umap

clustree(seurat.object, prefix = "RNA_snn_res.", node_colour = "sc3_stability") + 
    scale_color_continuous(low = 'red3', high = 'white')
png(filename = paste0(projectName, "_dim", ndims, "-clustree.png"), height = 800, width = 1600)
clustree(seurat.object, prefix = "RNA_snn_res.", node_colour = "sc3_stability") + 
    scale_color_continuous(low = 'red3', high = 'white')
dev.off()
quartz_off_screen 
                2 

Plot UMAP

tot.var <- percent.variance(seurat.object@reductions$pca@stdev, plot.var = FALSE, return.val = TRUE)
ndims <- length(which(cumsum(tot.var) <= 95))

seurat.object <- FindNeighbors(seurat.object, dims = 1:ndims)
Computing nearest neighbor graph
Computing SNN
seurat.object <- FindClusters(seurat.object, resolution = 0.5)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 478535

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8472
Number of communities: 9
Elapsed time: 4 seconds
seurat.object <- RunUMAP(seurat.object, dims = 1: ndims)
18:30:03 UMAP embedding parameters a = 0.9922 b = 1.112
18:30:03 Read 12059 rows and found 41 numeric columns
18:30:03 Using Annoy for neighbor search, n_neighbors = 30
18:30:03 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
18:30:05 Writing NN index file to temp file /var/folders/4f/fwrj6fnn1dn4g8wsf0zv563hjsvl24/T//RtmplnWyat/file14818712f8e56
18:30:05 Searching Annoy index using 1 thread, search_k = 3000
18:30:13 Annoy recall = 100%
18:30:13 Commencing smooth kNN distance calibration using 1 thread
18:30:16 Initializing from normalized Laplacian + noise
18:30:17 Commencing optimization for 200 epochs, with 524686 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
18:30:28 Optimization finished
for(x in c(0.5, 1, 1.5, 2, 2.5)){
    seurat.object <- FindClusters(seurat.object, resolution = x)
}
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 478535

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8472
Number of communities: 9
Elapsed time: 4 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 478535

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.7908
Number of communities: 17
Elapsed time: 4 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 478535

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.7591
Number of communities: 20
Elapsed time: 4 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 478535

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.7332
Number of communities: 26
Elapsed time: 3 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 12059
Number of edges: 478535

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.7121
Number of communities: 32
Elapsed time: 4 seconds
for (meta.col in colnames(seurat.object@meta.data)){
    if(grepl(pattern = ("RNA_snn_res"), x = meta.col)==TRUE){
        myplot <- DimPlot(seurat.object, 
                                            group.by = meta.col,
                                            reduction = "umap", 
                                            cols = color.palette
        ) + 
            ggtitle(paste0(projectName, " dim", ndims, "res", gsub("RNA_snn_res", "", meta.col) ))
        plot(myplot)
    }
}

Evaluate cluster stability

saveRDS(seurat.object, file = paste0(projectName, "_dim", ndims, ".RDS"))

Export for Biomark

Import biomark probe list

clustree(seurat.object, prefix = "RNA_snn_res.", node_colour = "sc3_stability") + 
    scale_color_continuous(low = 'red3', high = 'white')

probe.list <- read.table()
xprsn.mtx <- 

DGE

I think clusters from 80% variance at 0.5 and 1.0 resolution are most stable. We’ll do some statistics and DGE on that. Will also need to go back and play with SCTransform, since these are multiple cell types from multiple lanes. ## Load favourite dim reduction file

rds.file <- ""
seurat.object <- readRDS(rds.file)
ndims <- as.numeric(gsub("[^0-9]", "", stringr::str_split(rds.file, "_")[[1]][3]))

Set favourite resolution

object.res <- ".0.5"
Idents(seurat.object) <- paste0("RNA_snn_res", object.res)
length(levels(seurat.object@active.ident))

count number of filtered cells left from each population

# Number of filtered cells left in each pop
sapply(c("LSKm2", "CMPm2", "MEPm", "GMPm"), function(x) (c(nrow(seurat.object@meta.data[seurat.object@meta.data$orig.ident == x,]))))
par(mfrow = c(2, 2))
for (x in c("LSKm2", "CMPm2", "MEPm", "GMPm")){
    h = hist(seurat.object@meta.data[seurat.object@meta.data$orig.ident == x, 'percent.mt'], breaks = 30, plot = FALSE)
    h$density = h$counts/sum(h$counts)*100
    plot(h,freq=FALSE, main =  paste(x, "percent mitoC"), xlab = "percent mitoC", ylab = "Frequency")
}
par(mfrow = c(1,1))
VlnPlot(subset(seurat.object, subset = orig.ident == "MEPm"), 
                features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 1, pt.size = 0, fill.by = 'ident', flip = TRUE)

DGE w/ resolution = 0.5

Strt with comparing all clusters against all other clusters and write out cluster info calculate FindAllMarkers() for different idents and save to new file

# ident.list <- colnames(seurat.object@meta.data)[grepl("^RNA_snn", colnames(seurat.object@meta.data))]
ident.list <- c("RNA_snn_res.0.5", "RNA_snn_res.1")

for(tested.ident in ident.list){
    Idents(seurat.object) <- tested.ident
    all.markers <- FindAllMarkers(seurat.object)
    xlsx::write.xlsx(x = all.markers[,c("avg_log2FC", "p_val_adj", "cluster", "gene")], 
                                     file = paste0(projectName, "_FindALLMarkers_dim",ndims, "_allres.xlsx"), 
                                     sheetName = tested.ident, 
                                     col.names = TRUE, 
                                     row.names = FALSE, 
                                     append = TRUE)
}

Create FindAllMarkers() lists for GSEA

object.res.allmarkers <- FindAllMarkers(seurat.object)

Map HGNC symbols

Mouse2HumanTable <- Mouse2Human(object.res.allmarkers$gene)

HGNC <- with(Mouse2HumanTable, Mouse2HumanTable$HGNC[match(object.res.allmarkers$gene, Mouse2HumanTable$MGI)])
head(object.res.allmarkers)
object.res.allmarkers$HGNC <- HGNC
tail(object.res.allmarkers)
sig.res <- object.res.allmarkers[object.res.allmarkers$p_val_adj <= 0.05, ]
sig.res <- sig.res[c("avg_log2FC", "HGNC", "cluster")]
sig.res <- sig.res[!(sig.res$HGNC == "" | is.na(sig.res$HGNC)),] # GSEA will fail if there are any blanks or NAs in the table
sig.res <- sig.res[]
for(cluster in unique(sig.res$cluster)){
    print(paste("writing cluster", cluster))
    new.table <- sig.res[sig.res$cluster == cluster, c("HGNC", "avg_log2FC")]
    new.table <- new.table[order(-new.table$avg_log2FC), ]
    dir.create(paste0("RankList_res", object.res, "_findAll_hgnc/"), showWarnings = FALSE)
    write.table(new.table, file = paste0("RankList_res", object.res, "_findAll_hgnc/res", object.res, "cluster", cluster, ".rnk"), quote = FALSE, row.names = FALSE, col.names = TRUE, sep = "\t", )
    
}

calculate FindMarkers() that distinguish each cluster (might overlab between clusters)

# ident.list <- colnames(seurat.object@meta.data)[grepl("^RNA_snn", colnames(seurat.object@meta.data))]
ident.list <- c("RNA_snn_res.0.5", "RNA_snn_res.1")
for(tested.ident in ident.list){
    for (cluster in sort(as.numeric(levels(seurat.object@meta.data[[tested.ident]])))){
        cluster.markers <- FindMarkers(seurat.object, ident.1 = cluster)
        xlsx::write.xlsx(x = cluster.markers[,c("avg_log2FC", "p_val_adj")], 
                                         file = paste0(projectName, "_FindMarkers_dim", ndims, gsub("RNA_snn_", "", tested.ident), ".xlsx"), 
                                         sheetName = paste0("clst", cluster), 
                                         col.names = TRUE, 
                                         row.names = TRUE, 
                                         append = TRUE)
    }
}
for (cluster in sort(as.numeric(levels(seurat.object@meta.data[paste0("RNA_snn_res", object.res)])))){
    cluster.markers <- FindMarkers(seurat.object, ident.1 = cluster)
    xlsx::write.xlsx(x = cluster.markers[,c("avg_log2FC", "p_val_adj")], 
                                     file = paste0(projectName, "_FindMarkers_dim", ndims, "res", object.res, ".xlsx"), 
                                     sheetName = paste0("clst", cluster), 
                                     col.names = TRUE, 
                                     row.names = TRUE, 
                                     append = TRUE)
}

Notes on cluster stability

Cluster stability could be influenced by: * cells in each population (cellranger v6 includes more cells than cellranger v1, especially in MEP) * dimensionality is incorrect * ScaleData didnt account for regression factors (e.g., “nCounts_RNA” or “nFeatures_RNA”) * Did not consider cell cycle * Incorrect normalization/scaling method * Clustering is too strict or not strict enough * neighborhood analysis used wrong parameters * Should include mitoC filter (there’s a chunk of MEP w/ mitoC @ ~40%) * SCTransform accounts better for sources of variability

LS0tCnRpdGxlOiAiQ01QX3NldXJhdCIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGZpZ19oZWlnaHQ6IDYKLS0tCgpDcmVhdGluZyBuZXcgcGlwZWxpbmUgdXNpbmcgc2V1cmF0IHY0LjAuMiBhdmFpbGFibGUgMjAyMS4wNi4yMyAgCkltcG9ydGFudCBub3RlczoKCiogRklMVEVSSU5HIG9uIGBwZXJjZW50Lm10YCwgYnV0IE5PVCByZWdyZXNzaW5nIG9uIGBwZXJjZW50Lm10YAoqIFJlZ3Jlc3Npbmcgb24gYG5Db3VudHNfUk5BYCBhbmQgYG5GZWF0dXJlX1JOQWAKCiMgU2V0dXAKIyMgTG9hZCBsaWJyYXJpZXMgcmVxdWlyZWQgZm9yIFNldXJhdHY0CmBgYHtyIHNldHVwLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpciA9ICJ+L0Rlc2t0b3AvMTBYR2Vub21pY3NEYXRhL21zQWdncl9zY1JOQVNlcS9JbmRpdmlkdWFsUG9wcy8iKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShjbHVzdHJlZSkKYGBgCiMjIFNvdXJjZSBmdW5jdGlvbnMKYGBge3J9CnNvdXJjZSgifi9EZXNrdG9wLzEwWEdlbm9taWNzRGF0YS9tc0FnZ3Jfc2NSTkFTZXEvUkZ1bmN0aW9ucy9yZWFkXzEwWEdlbm9taWNzX2RhdGEuUiIpCnNvdXJjZSgifi9EZXNrdG9wLzEwWEdlbm9taWNzRGF0YS9tc0FnZ3Jfc2NSTkFTZXEvUkZ1bmN0aW9ucy9QZXJjZW50VmFyaWFuY2UuUiIpCnNvdXJjZSgifi9EZXNrdG9wLzEwWEdlbm9taWNzRGF0YS9tc0FnZ3Jfc2NSTkFTZXEvUkZ1bmN0aW9ucy9Db2xvclBhbGV0dGUuUiIpCnNvdXJjZSgifi9EZXNrdG9wLzEwWEdlbm9taWNzRGF0YS9tc0FnZ3Jfc2NSTkFTZXEvUkZ1bmN0aW9ucy9Nb3VzZTJIdW1hbl9pZGNvbnZlcnNpb24uUiIpCmBgYAoKCiMgQSBub3RlIGFib3V0IHVzaW5nIFNDVHJhbnNmb3JtIHZlcnN1cyBgU2NhbGVEYXRhYApodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvMy4xMC93b3JrZmxvd3MvdmlnbmV0dGVzL3NpbXBsZVNpbmdsZUNlbGwvaW5zdC9kb2MvYmF0Y2guaHRtbCM2Ml9mb3JfZ2VuZS1iYXNlZF9hbmFseXNlcwo+WW91IGNhbiBhbHNvIG5vcm1hbGl6ZSBhbmQgc2NhbGUgZGF0YSBmb3IgdGhlIFJOQSBhc3NheS4gVGhlcmUgYXJlIG51bWVyb3VzIHJlc291cmNlcyBvbiB0aGlzLCBidXQgQWFyb24gTHVuIGRlc2NyaWJlcyB3aHkgdGhlIG9yaWdpbmFsIGxvZy1ub3JtYWxpemVkIHZhbHVlcyBzaG91bGQgYmUgdXNlZCBmb3IgREUgYW5kIHZpc3VhbGl6YXRpb25zIG9mIGV4cHJlc3Npb24gcXVpdGUgd2VsbCBoZXJlOgo+Cj5Gb3IgZ2VuZS1iYXNlZCBwcm9jZWR1cmVzIGxpa2UgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gKERFKSBhbmFseXNlcyBvciBnZW5lIG5ldHdvcmsgY29uc3RydWN0aW9uLCBpdCBpcyBkZXNpcmFibGUgdG8gdXNlIHRoZSBvcmlnaW5hbCBsb2ctZXhwcmVzc2lvbiB2YWx1ZXMgb3IgY291bnRzLiBUaGUgY29ycmVjdGVkIHZhbHVlcyBhcmUgb25seSB1c2VkIHRvIG9idGFpbiBjZWxsLWxldmVsIHJlc3VsdHMgc3VjaCBhcyBjbHVzdGVycyBvciB0cmFqZWN0b3JpZXMuIEJhdGNoIGVmZmVjdHMgYXJlIGhhbmRsZWQgZXhwbGljaXRseSB1c2luZyBibG9ja2luZyB0ZXJtcyBvciB2aWEgYSBtZXRhLWFuYWx5c2lzIGFjcm9zcyBiYXRjaGVzLiBXZSBkbyBub3QgdXNlIHRoZSBjb3JyZWN0ZWQgdmFsdWVzIGRpcmVjdGx5IGluIGdlbmUtYmFzZWQgYW5hbHlzZXMsIGZvciB2YXJpb3VzIHJlYXNvbnM6Cj4KPkl0IGlzIHVzdWFsbHkgaW5hcHByb3ByaWF0ZSB0byBwZXJmb3JtIERFIGFuYWx5c2VzIG9uIGJhdGNoLWNvcnJlY3RlZCB2YWx1ZXMsIGR1ZSB0byB0aGUgZmFpbHVyZSB0byBtb2RlbCB0aGUgdW5jZXJ0YWludHkgb2YgdGhlIGNvcnJlY3Rpb24uIFRoaXMgdXN1YWxseSByZXN1bHRzIGluIGxvc3Mgb2YgdHlwZSBJIGVycm9yIGNvbnRyb2wsIGkuZS4sIG1vcmUgZmFsc2UgcG9zaXRpdmVzIHRoYW4gZXhwZWN0ZWQuCj4KPlRoZSBjb3JyZWN0aW9uIGRvZXMgbm90IHByZXNlcnZlIHRoZSBtZWFuLXZhcmlhbmNlIHJlbGF0aW9uc2hpcC4gQXBwbGljYXRpb25zIG9mIGNvbW1vbiBERSBtZXRob2RzIGxpa2UgZWRnZVIgb3IgbGltbWEgYXJlIHVubGlrZWx5IHRvIGJlIHZhbGlkLgo+Cj5CYXRjaCBjb3JyZWN0aW9uIG1heSAoY29ycmVjdGx5KSByZW1vdmUgYmlvbG9naWNhbCBkaWZmZXJlbmNlcyBiZXR3ZWVuIGJhdGNoZXMgaW4gdGhlIGNvdXJzZSBvZiBtYXBwaW5nIGFsbCBjZWxscyBvbnRvIGEgY29tbW9uIGNvb3JkaW5hdGUgc3lzdGVtLiBSZXR1cm5pbmcgdG8gdGhlIHVuY29ycmVjdGVkIGV4cHJlc3Npb24gdmFsdWVzIHByb3ZpZGVzIGFuIG9wcG9ydHVuaXR5IGZvciBkZXRlY3Rpbmcgc3VjaCBkaWZmZXJlbmNlcyBpZiB0aGV5IGFyZSBvZiBpbnRlcmVzdC4gQ29udmVyc2VseSwgaWYgdGhlIGJhdGNoIGNvcnJlY3Rpb24gbWFkZSBhIG1pc3Rha2UsIHRoZSB1c2Ugb2YgdGhlIHVuY29ycmVjdGVkIGV4cHJlc3Npb24gdmFsdWVzIHByb3ZpZGVzIGFuIGltcG9ydGFudCBzYW5pdHkgY2hlY2suCj4KPkluIGFkZGl0aW9uLCB0aGUgbm9ybWFsaXplZCB2YWx1ZXMgaW4gU0NUIGFuZCBpbnRlZ3JhdGVkIGFzc2F5cyBkb24ndCBuZWNlc3NhcnkgY29ycmVzcG9uZCB0byBwZXItZ2VuZSBleHByZXNzaW9uIHZhbHVlcyBhbnl3YXksIHJhdGhlciBjb250YWluaW5nIHJlc2lkdWFscyAoaW4gdGhlIGNhc2Ugb2YgdGhlIHNjYWxlLmRhdGEgc2xvdCBmb3IgZWFjaCkuCgoKCk1lc3Mgd2l0aCBob3cgdG8gbG9hZCA0IGNlbGwgcG9wdWxhdGlvbnMgaW50byBzaW5nbGUgc2V1cmF0IG9iamVjdAoKCgpTRVQgU0VFRD8/Pz8/ISEhISEKCiMjIFNldCBnbG9iYWwgdmFyaWFibGVzCgpgYGB7cn0KcHJvamVjdE5hbWUgPC0gIkNNUFN1YnBvcCIKamFja3N0cmF3LmRpbSA8LSA0MApgYGAKCiMjIFN0b3JlIHNlc3Npb24gaW5mbwpgYGB7cn0Kc2Vzc2lvbkluZm8uZmlsZW5hbWUgPC0gcGFzdGUwKHByb2plY3ROYW1lLCAiX3Nlc3Npb25JbmZvLnR4dCIpCnNpbmsoc2Vzc2lvbkluZm8uZmlsZW5hbWUpCnNlc3Npb25JbmZvKCkKc2luaygpCmBgYAoKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CnNldHdkKCJ+L0Rlc2t0b3AvMTBYR2Vub21pY3NEYXRhL2NlbGxSYW5nZXIvIikgIyB0ZW1wb3JhcmlseSBjaGFuZ2luZyB3ZCBvbmx5IHdvcmtzIGlmIHlvdSBydW4gdGhlIGVudGlyZSBjaHVuayBhdCBvbmNlCmRhdGFfZmlsZS5saXN0IDwtIHJlYWRfMTBYR2Vub21pY3NfZGF0YShzYW1wbGUubGlzdCA9IGMoIkNNUG0yIikpCmRhdGEub2JqZWN0PC1SZWFkMTBYKGRhdGFfZmlsZS5saXN0KQpgYGAKCgoKYGBge3J9CnNldXJhdC5vYmplY3Q8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gZGF0YS5vYmplY3QsIG1pbi5jZWxscyA9IDMsIG1pbi5nZW5lcyA9IDIwMCwgcHJvamVjdCA9IHByb2plY3ROYW1lKQpgYGAKCkNsZWFuIHVwIHRvIGZyZWUgbWVtb3J5CgpgYGB7cn0KcmVtb3ZlKGRhdGEub2JqZWN0KQpgYGAKCgpBZGQgbWl0b2Nob25kcmlhbCBtZXRhZGF0YSBhbmQgcGxvdCBzb21lIGJhc2ljIGZlYXR1cmVzCmBgYHtyfQpzZXVyYXQub2JqZWN0W1sicGVyY2VudC5tdCJdXSA8LSBQZXJjZW50YWdlRmVhdHVyZVNldChzZXVyYXQub2JqZWN0LCBwYXR0ZXJuID0gIl5tdC0iKQpWbG5QbG90KHNldXJhdC5vYmplY3QsIGZlYXR1cmVzID0gYygibkZlYXR1cmVfUk5BIiwgIm5Db3VudF9STkEiLCAicGVyY2VudC5tdCIpLCBuY29sID0gMywgcHQuc2l6ZSA9IDAsIGZpbGwuYnkgPSAnb3JpZy5pZGVudCcsICkKYGBgCgoKYGBge3IgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9Mn0KcGxvdDEgPC0gRmVhdHVyZVNjYXR0ZXIoc2V1cmF0Lm9iamVjdCwgZmVhdHVyZTEgPSAibkNvdW50X1JOQSIsIGZlYXR1cmUyID0gInBlcmNlbnQubXQiLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IiwgcHQuc2l6ZSA9IDAuMDEpCnBsb3QyIDwtIEZlYXR1cmVTY2F0dGVyKHNldXJhdC5vYmplY3QsIGZlYXR1cmUxID0gIm5Db3VudF9STkEiLCBmZWF0dXJlMiA9ICJuRmVhdHVyZV9STkEiLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IiwgcHQuc2l6ZSA9IDAuMDEpCnBsb3QxICsgcGxvdDIKYGBgCgoKcmVtb3ZlIGxvdyBxdWFsaXR5IGNlbGxzCnJlcXVpcmU6IG5GZWF0dXJlX1JOQSBiZXR3ZWVuIDIwMCBhbmQgNDAwMCAoaW5jbHVzaXZlKQpyZXF1aXJlOiBwZXJjZW50Lm10IDw9IDUKCmBgYHtyfQpwcmludChwYXN0ZSgib3JpZ2luYWwgb2JqZWN0OiIsIG5yb3coc2V1cmF0Lm9iamVjdEBtZXRhLmRhdGEpLCAiY2VsbHMiLCBzZXAgPSAiICIpKQpzZXVyYXQub2JqZWN0IDwtIHN1YnNldChzZXVyYXQub2JqZWN0LCAKCQkJCQkJCQkJCQkJc3Vic2V0ID0gbkZlYXR1cmVfUk5BID49MjAwICYgCgkJCQkJCQkJCQkJCQluRmVhdHVyZV9STkEgPD0gNDAwMCAmIAoJCQkJCQkJCQkJCQkJcGVyY2VudC5tdCA8PSA1CikKcHJpbnQocGFzdGUoIm5ldyBvYmplY3Q6IiwgbnJvdyhzZXVyYXQub2JqZWN0QG1ldGEuZGF0YSksICJjZWxscyIsIHNlcCA9ICIgIikpCmBgYAoKCgojIyBOb3JtYWxpemF0aW9uCgpTdHJ1Z2dsaW5nIHRvIHdyYXAgbXkgaGVhZCBhcm91bmQgdGhpcyBvbmUuIEl0IHNlZW1zIHRoYXQgU0NUcmFuc2Zvcm0gaXMgYmVzdCBmb3IgYmF0Y2ggY29ycmVjdGlvbiwgYnV0IGBOb3JtYWxpemVEYXRhYCBhbmQgYFNjYWxlRGF0YWAgYXJlIGJlc3QgZm9yIERHRS4gU2V2ZXJhbCB2aWduZXR0ZXMgaGF2ZSBwZXJmb3JtZWQgYm90aAoKYHNlbGVjdGlvbi5tZXRob2QJCkhvdyB0byBjaG9vc2UgdG9wIHZhcmlhYmxlIGZlYXR1cmVzLiBDaG9vc2Ugb25lIG9mIDoKCnZzdDogRmlyc3QsIGZpdHMgYSBsaW5lIHRvIHRoZSByZWxhdGlvbnNoaXAgb2YgbG9nKHZhcmlhbmNlKSBhbmQgbG9nKG1lYW4pIHVzaW5nIGxvY2FsIHBvbHlub21pYWwgcmVncmVzc2lvbiAobG9lc3MpLiBUaGVuIHN0YW5kYXJkaXplcyB0aGUgZmVhdHVyZSB2YWx1ZXMgdXNpbmcgdGhlIG9ic2VydmVkIG1lYW4gYW5kIGV4cGVjdGVkIHZhcmlhbmNlIChnaXZlbiBieSB0aGUgZml0dGVkIGxpbmUpLiBGZWF0dXJlIHZhcmlhbmNlIGlzIHRoZW4gY2FsY3VsYXRlZCBvbiB0aGUgc3RhbmRhcmRpemVkIHZhbHVlcyBhZnRlciBjbGlwcGluZyB0byBhIG1heGltdW0gKHNlZSBjbGlwLm1heCBwYXJhbWV0ZXIpLgoKbWVhbi52YXIucGxvdCAobXZwKTogRmlyc3QsIHVzZXMgYSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgYXZlcmFnZSBleHByZXNzaW9uIChtZWFuLmZ1bmN0aW9uKSBhbmQgZGlzcGVyc2lvbiAoZGlzcGVyc2lvbi5mdW5jdGlvbikgZm9yIGVhY2ggZmVhdHVyZS4gTmV4dCwgZGl2aWRlcyBmZWF0dXJlcyBpbnRvIG51bS5iaW4gKGRlYWZ1bHQgMjApIGJpbnMgYmFzZWQgb24gdGhlaXIgYXZlcmFnZSBleHByZXNzaW9uLCBhbmQgY2FsY3VsYXRlcyB6LXNjb3JlcyBmb3IgZGlzcGVyc2lvbiB3aXRoaW4gZWFjaCBiaW4uIFRoZSBwdXJwb3NlIG9mIHRoaXMgaXMgdG8gaWRlbnRpZnkgdmFyaWFibGUgZmVhdHVyZXMgd2hpbGUgY29udHJvbGxpbmcgZm9yIHRoZSBzdHJvbmcgcmVsYXRpb25zaGlwIGJldHdlZW4gdmFyaWFiaWxpdHkgYW5kIGF2ZXJhZ2UgZXhwcmVzc2lvbi4KCmRpc3BlcnNpb24gKGRpc3ApOiBzZWxlY3RzIHRoZSBnZW5lcyB3aXRoIHRoZSBoaWdoZXN0IGRpc3BlcnNpb24gdmFsdWVzYAoKCgoKYGBge3J9CnNldXJhdC5vYmplY3QgPC0gTm9ybWFsaXplRGF0YShzZXVyYXQub2JqZWN0LCBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiLCBzY2FsZS5mYWN0b3IgPSAxMDAwMCkKYGBgCgoKCgoKRmluZCB2YXJpYWJsZSBmZWF0dXJlcwpgYGB7ciBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gMn0Kc2V1cmF0Lm9iamVjdCA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhzZXVyYXQub2JqZWN0LCBzZWxlY3Rpb24ubWV0aG9kID0gInZzdCIsIG5mZWF0dXJlcyA9IDIwMDApCnRvcDEwIDwtIGhlYWQoVmFyaWFibGVGZWF0dXJlcyhzZXVyYXQub2JqZWN0KSwgMTApCnBsb3QxIDwtIFZhcmlhYmxlRmVhdHVyZVBsb3Qoc2V1cmF0Lm9iamVjdCkKcGxvdDIgPC0gTGFiZWxQb2ludHMocGxvdCA9IHBsb3QxLCBwb2ludHMgPSB0b3AxMCwgcmVwZWwgPSBUUlVFKQpwbG90MSArIHBsb3QyCmBgYAoKU2NhbGUgZGF0YSAobGluZWFyIHRyYW5zZm9ybWF0aW9uKQoKYGBge3J9CmFsbC5nZW5lcyA8LSByb3duYW1lcyhzZXVyYXQub2JqZWN0KQpzZXVyYXQub2JqZWN0IDwtIFNjYWxlRGF0YShzZXVyYXQub2JqZWN0LCBmZWF0dXJlcyA9IGFsbC5nZW5lcywgdmFycy50by5yZWdyZXNzID0gYygibkNvdW50X1JOQSIsICJuRmVhdHVyZV9STkEiKSkKYGBgCgoKIyMjIFNhdmUgcHJvZ3Jlc3MKCmBgYHtyfQojIHNhdmUuaW1hZ2UoZmlsZSA9IHBhc3RlMChwcm9qZWN0TmFtZSwgJy5SRGF0YScpKQpzYXZlUkRTKHNldXJhdC5vYmplY3QsIGZpbGUgPSBwYXN0ZTAocHJvamVjdE5hbWUsICJfcmF3LlJEUyIpKQpgYGAKCgojIyBQQ0EKCmxpbmVhciBkaW1lbnNpb25hbCByZWR1Y3Rpb24uIERlZmF1bHQgYXJlIGJhc2VkIG9uIFZhcmlhYmxlRmVhdHVyZXMsIGJ1dCBjYW4gYmUgY2hhbmdlZAoKYGBge3J9CnNldXJhdC5vYmplY3QgPC0gUnVuUENBKHNldXJhdC5vYmplY3QsIGZlYXR1cmVzID0gVmFyaWFibGVGZWF0dXJlcyhvYmplY3QgPSBzZXVyYXQub2JqZWN0KSkKYGBgClBsb3QgcmVzdWx0cwpgYGB7ciBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD00fQpWaXpEaW1Mb2FkaW5ncyhzZXVyYXQub2JqZWN0LCBkaW1zID0gMTo2LCBuZmVhdHVyZXMgPSAxMCwgcmVkdWN0aW9uID0gInBjYSIsIG5jb2wgPSAyKQpgYGAKCkRpbVBsb3QgY29sb3JlZCBieSBvcmlnLmlkZW50CmBgYHtyfQpEaW1QbG90KHNldXJhdC5vYmplY3QsIHJlZHVjdGlvbiA9ICJwY2EiLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IikKYGBgCkxldCdzIHB1dCBpbiBhIGNvbmNlcnRlZCBlZmZvcnQgdG8gcGljayB0aGUgcmlnaHQgZGltZW5zaW9uYWxpdHkgdXNpbmcgdGhlIG5ld2VzdCBzb2Z0d2FyZQpgYGB7cn0KIyBqYWNrc3RyYXcuZGltIDwtIDQwCiMgc2V1cmF0Lm9iamVjdCA8LSBKYWNrU3RyYXcoc2V1cmF0Lm9iamVjdCwgbnVtLnJlcGxpY2F0ZSA9IDEwMCwgZGltcyA9IGphY2tzdHJhdy5kaW0pICNydW5zIH41MCBtaW4KIyBzZXVyYXQub2JqZWN0IDwtIFNjb3JlSmFja1N0cmF3KHNldXJhdC5vYmplY3QsIGRpbXMgPSAxOmphY2tzdHJhdy5kaW0pCiMgc2F2ZS5pbWFnZShwYXN0ZTAocHJvamVjdE5hbWUsICIuUkRhdGEiKSkKYGBgCkRyYXcgZGltLnJlZHVjdGlvbiBwbG90cwpgYGB7cn0KIyBKYWNrU3RyYXdQbG90KHNldXJhdC5vYmplY3QsIGRpbXMgPSAyNTozNikKYGBgCmBgYHtyLCBmaWd1cmVzLXNpZGUsIGZpZy5zaG93PSdob2xkJywgb3V0LndpZHRoPSI1MCUifQpFbGJvd1Bsb3Qoc2V1cmF0Lm9iamVjdCwgbmRpbXMgPSA1MCkKcGVyY2VudC52YXJpYW5jZShzZXVyYXQub2JqZWN0QHJlZHVjdGlvbnMkcGNhQHN0ZGV2KQpgYGAKTnVtYmVyIG9mIFBDcyBkZXNjcmliaW5nIFglIG9mIHZhcmlhbmNlCmBgYHtyfQp0b3QudmFyIDwtIHBlcmNlbnQudmFyaWFuY2Uoc2V1cmF0Lm9iamVjdEByZWR1Y3Rpb25zJHBjYUBzdGRldiwgcGxvdC52YXIgPSBGQUxTRSwgcmV0dXJuLnZhbCA9IFRSVUUpCnBhc3RlMCgiTnVtIHBjcyBmb3IgODAlIHZhcmlhbmNlOiAiLCBsZW5ndGgod2hpY2goY3Vtc3VtKHRvdC52YXIpIDw9IDgwKSkpCnBhc3RlMCgiTnVtIHBjcyBmb3IgODUlIHZhcmlhbmNlOiAiLCBsZW5ndGgod2hpY2goY3Vtc3VtKHRvdC52YXIpIDw9IDg1KSkpCnBhc3RlMCgiTnVtIHBjcyBmb3IgOTAlIHZhcmlhbmNlOiAiLCBsZW5ndGgod2hpY2goY3Vtc3VtKHRvdC52YXIpIDw9IDkwKSkpCnBhc3RlMCgiTnVtIHBjcyBmb3IgOTUlIHZhcmlhbmNlOiAiLCBsZW5ndGgod2hpY2goY3Vtc3VtKHRvdC52YXIpIDw9IDk1KSkpCgpgYGAKClBlcmNlbnQgb2YgUENzIGRlc2NyaWJpbmcgWCUgb2YgdmFyaWFuY2UgKHRyYW5zY3JpYmVkIGZyb20gYWJvdmUgY2VsbCBiZWNhdXNlIEkgZG9uJ3Qga25vdyBob3cgdG8gZnJlZXplIHJlc3VsdHMpCgpOdW0gcGNzIGZvciA4MCUgdmFyaWFuY2U6IDEyCk51bSBwY3MgZm9yIDg1JSB2YXJpYW5jZTogMTgKTnVtIHBjcyBmb3IgOTAlIHZhcmlhbmNlOiAyNgpOdW0gcGNzIGZvciA5NSUgdmFyaWFuY2U6IDM3CgojIElEIGNsdXN0ZXJzIGJhc2VkIG9uIGRpZmZlcmVudCB2YXJpYW5jZXMKIyMgRGVzY3JpYmUgODAlIG9mIHZhcmlhbmNlIHdpdGggMTIgZGltZW5zaW9ucwoKIyMjIE5laWdoYm9yaG9vZCBhbmQgdW1hcApzZXQgdG90YWwudmFyIDwtIDgwJQpgYGB7cn0KdG90LnZhciA8LSBwZXJjZW50LnZhcmlhbmNlKHNldXJhdC5vYmplY3RAcmVkdWN0aW9ucyRwY2FAc3RkZXYsIHBsb3QudmFyID0gRkFMU0UsIHJldHVybi52YWwgPSBUUlVFKQpuZGltcyA8LSBsZW5ndGgod2hpY2goY3Vtc3VtKHRvdC52YXIpIDw9IDgwKSkKCnNldXJhdC5vYmplY3QgPC0gRmluZE5laWdoYm9ycyhzZXVyYXQub2JqZWN0LCBkaW1zID0gMTpuZGltcykKc2V1cmF0Lm9iamVjdCA8LSBGaW5kQ2x1c3RlcnMoc2V1cmF0Lm9iamVjdCwgcmVzb2x1dGlvbiA9IDAuNSkKc2V1cmF0Lm9iamVjdCA8LSBSdW5VTUFQKHNldXJhdC5vYmplY3QsIGRpbXMgPSAxOiBuZGltcykKCmBgYApQbG90IFVNQVAKCmBgYHtyfQpmb3IoeCBpbiBjKDAuNSwgMSwgMS41LCAyLCAyLjUpKXsKCXNldXJhdC5vYmplY3QgPC0gRmluZENsdXN0ZXJzKHNldXJhdC5vYmplY3QsIHJlc29sdXRpb24gPSB4KQp9CmBgYAoKYGBge3J9CmZvciAobWV0YS5jb2wgaW4gY29sbmFtZXMoc2V1cmF0Lm9iamVjdEBtZXRhLmRhdGEpKXsKCWlmKGdyZXBsKHBhdHRlcm4gPSAoIlJOQV9zbm5fcmVzIiksIHggPSBtZXRhLmNvbCk9PVRSVUUpewoJCW15cGxvdCA8LSBEaW1QbG90KHNldXJhdC5vYmplY3QsIAoJCQkJCQkJCQkJCWdyb3VwLmJ5ID0gbWV0YS5jb2wsCgkJCQkJCQkJCQkJcmVkdWN0aW9uID0gInVtYXAiLCAKCQkJCQkJCQkJCQljb2xzID0gY29sb3IucGFsZXR0ZQoJCSkgKyAKCQkJZ2d0aXRsZShwYXN0ZTAocHJvamVjdE5hbWUsICIgZGltIiwgbmRpbXMsICJyZXMiLCBnc3ViKCJSTkFfc25uX3JlcyIsICIiLCBtZXRhLmNvbCkgKSkKCQlwbG90KG15cGxvdCkKCX0KfQpgYGAKCmBgYHtyfQpzYXZlUkRTKHNldXJhdC5vYmplY3QsIGZpbGUgPSBwYXN0ZTAocHJvamVjdE5hbWUsICJfZGltIiwgbmRpbXMsICIuUkRTIikpCmBgYAoKIyMjIEV2YWx1YXRlIGNsdXN0ZXIgc3RhYmlsaXR5CmBgYHtyIGZpZy5oZWlnaHQ9NX0KY2x1c3RyZWUoc2V1cmF0Lm9iamVjdCwgcHJlZml4ID0gIlJOQV9zbm5fcmVzLiIsIG5vZGVfY29sb3VyID0gInNjM19zdGFiaWxpdHkiKSArIAoJc2NhbGVfY29sb3JfY29udGludW91cyhsb3cgPSAncmVkMycsIGhpZ2ggPSAnd2hpdGUnKQpgYGAKCgojIyBEZXNjcmliZSA4NSUgb2YgdmFyaWFuY2UgCgojIyMgTmVpZ2hib3Job29kIGFuZCB1bWFwCnNldCB0b3RhbC52YXIgPC0gODUlCmBgYHtyfQp0b3QudmFyIDwtIHBlcmNlbnQudmFyaWFuY2Uoc2V1cmF0Lm9iamVjdEByZWR1Y3Rpb25zJHBjYUBzdGRldiwgcGxvdC52YXIgPSBGQUxTRSwgcmV0dXJuLnZhbCA9IFRSVUUpCm5kaW1zIDwtIGxlbmd0aCh3aGljaChjdW1zdW0odG90LnZhcikgPD0gODUpKQoKc2V1cmF0Lm9iamVjdCA8LSBGaW5kTmVpZ2hib3JzKHNldXJhdC5vYmplY3QsIGRpbXMgPSAxOm5kaW1zKQpzZXVyYXQub2JqZWN0IDwtIEZpbmRDbHVzdGVycyhzZXVyYXQub2JqZWN0LCByZXNvbHV0aW9uID0gMC41KQpzZXVyYXQub2JqZWN0IDwtIFJ1blVNQVAoc2V1cmF0Lm9iamVjdCwgZGltcyA9IDE6IG5kaW1zKQoKYGBgClBsb3QgVU1BUAoKYGBge3J9CmZvcih4IGluIGMoMC41LCAxLCAxLjUsIDIsIDIuNSkpewoJc2V1cmF0Lm9iamVjdCA8LSBGaW5kQ2x1c3RlcnMoc2V1cmF0Lm9iamVjdCwgcmVzb2x1dGlvbiA9IHgpCn0KYGBgCgpgYGB7cn0KZm9yIChtZXRhLmNvbCBpbiBjb2xuYW1lcyhzZXVyYXQub2JqZWN0QG1ldGEuZGF0YSkpewoJaWYoZ3JlcGwocGF0dGVybiA9ICgiUk5BX3Nubl9yZXMiKSwgeCA9IG1ldGEuY29sKT09VFJVRSl7CgkJbXlwbG90IDwtIERpbVBsb3Qoc2V1cmF0Lm9iamVjdCwgCgkJCQkJCQkJCQkJZ3JvdXAuYnkgPSBtZXRhLmNvbCwKCQkJCQkJCQkJCQlyZWR1Y3Rpb24gPSAidW1hcCIsIAoJCQkJCQkJCQkJCWNvbHMgPSBjb2xvci5wYWxldHRlCgkJKSArIAoJCQlnZ3RpdGxlKHBhc3RlMChwcm9qZWN0TmFtZSwgIiBkaW0iLCBuZGltcywgInJlcyIsIGdzdWIoIlJOQV9zbm5fcmVzIiwgIiIsIG1ldGEuY29sKSApKQoJCXBsb3QobXlwbG90KQoJfQp9CmBgYAoKYGBge3J9CnNhdmVSRFMoc2V1cmF0Lm9iamVjdCwgZmlsZSA9IHBhc3RlMChwcm9qZWN0TmFtZSwgIl9kaW0iLCBuZGltcywgIi5SRFMiKSkKYGBgCgoKIyMjIEV2YWx1YXRlIGNsdXN0ZXIgc3RhYmlsaXR5Ck11c3QgZW5zdXJlIHdlIGhhdmUgdGhlIHJpZ2h0IGNsdXN0ZXIgc3RhYmlsaXR5LCB0aGF0IGlzLCBjZWxscyB0aGF0IHN0YXJ0IGluIHRoZSBzYW1lIGNsdXN0ZXIgdGVuZCB0byBzdGF5IGluIHRoZSBzYW1lIGNsdXN0ZXIuIElmIHlvdXIgZGF0YSBpcyBvdmVyLWNsdXN0ZXJlZCwgY2VsbHMgd2lsbCBib3VuY2UgYmV0d2VlbiBncm91cHMuCgpGb2xsb3dpbmcgW3RoaXMgdHV0b3JpYWwgYnkgTWF0dCBPLl0uaHR0cHM6Ly90b3dhcmRzZGF0YXNjaWVuY2UuY29tLzEwLXRpcHMtZm9yLWNob29zaW5nLXRoZS1vcHRpbWFsLW51bWJlci1vZi1jbHVzdGVycy0yNzdlOTNkNzJkOTIuClByZXZpb3VzbHkgbXkgZmF2b3VyaXRlIGhhcyBiZWVuIENsdXN0cmVlLCB3aGljaCBnaXZlcyBhIG5pY2UgdmlzdWFsCk5COiBGb3Igc29tZSByZWFzb24gYGNsdXN0cmVlOjpjbHVzdHJlZSgpYCBkaWRuJ3Qgd29yaywgd2hlcmVhcyBgbGlicmFyeShjbHVzdHJlZSlgIGZvbGxvd2VkIGJ5IGBjbHVzdHJlZSgpYCBkaWQuCgpgYGB7ciBmaWcuaGVpZ2h0PTV9CmNsdXN0cmVlKHNldXJhdC5vYmplY3QsIHByZWZpeCA9ICJSTkFfc25uX3Jlcy4iLCBub2RlX2NvbG91ciA9ICJzYzNfc3RhYmlsaXR5IikgKyAKCXNjYWxlX2NvbG9yX2NvbnRpbnVvdXMobG93ID0gJ3JlZDMnLCBoaWdoID0gJ3doaXRlJykKYGBgCgoKCgoKCgoKCiMjIERlc2NyaWJlIDkwJSBvZiB2YXJpYW5jZSAKCiMjIyBOZWlnaGJvcmhvb2QgYW5kIHVtYXAKYGBge3J9CnRvdC52YXIgPC0gcGVyY2VudC52YXJpYW5jZShzZXVyYXQub2JqZWN0QHJlZHVjdGlvbnMkcGNhQHN0ZGV2LCBwbG90LnZhciA9IEZBTFNFLCByZXR1cm4udmFsID0gVFJVRSkKbmRpbXMgPC0gbGVuZ3RoKHdoaWNoKGN1bXN1bSh0b3QudmFyKSA8PSA5MCkpCgpzZXVyYXQub2JqZWN0IDwtIEZpbmROZWlnaGJvcnMoc2V1cmF0Lm9iamVjdCwgZGltcyA9IDE6bmRpbXMpCnNldXJhdC5vYmplY3QgPC0gRmluZENsdXN0ZXJzKHNldXJhdC5vYmplY3QsIHJlc29sdXRpb24gPSAwLjUpCnNldXJhdC5vYmplY3QgPC0gUnVuVU1BUChzZXVyYXQub2JqZWN0LCBkaW1zID0gMTogbmRpbXMpCgpgYGAKUGxvdCBVTUFQCgpgYGB7cn0KZm9yKHggaW4gYygwLjUsIDEsIDEuNSwgMiwgMi41KSl7CglzZXVyYXQub2JqZWN0IDwtIEZpbmRDbHVzdGVycyhzZXVyYXQub2JqZWN0LCByZXNvbHV0aW9uID0geCkKfQpgYGAKCmBgYHtyfQpmb3IgKG1ldGEuY29sIGluIGNvbG5hbWVzKHNldXJhdC5vYmplY3RAbWV0YS5kYXRhKSl7CglpZihncmVwbChwYXR0ZXJuID0gIlJOQV9zbm5fcmVzIiwgeCA9IG1ldGEuY29sKT09VFJVRSB8IGdyZXBsKHBhdHRlcm4gPSAib3JpZy5pZGVudCIsIHggPSBtZXRhLmNvbCk9PVRSVUUpewoJCW15cGxvdCA8LSBEaW1QbG90KHNldXJhdC5vYmplY3QsIAoJCQkJCQkJCQkJCWdyb3VwLmJ5ID0gbWV0YS5jb2wsCgkJCQkJCQkJCQkJcmVkdWN0aW9uID0gInVtYXAiLCAKCQkJCQkJCQkJCQlwdC5zaXplID0gMSwKCQkJCQkJCQkJCQljb2xzID0gY29sb3IucGFsZXR0ZSkgKyAKCQkJZ2d0aXRsZShwYXN0ZTAocHJvamVjdE5hbWUsICIgZGltIiwgbmRpbXMsICJyZXMuIiwgZ3N1YigiUk5BX3Nubl9yZXMuIiwgIiIsIG1ldGEuY29sKSApKQoJCXBsb3QobXlwbG90KQoJCXBuZyhmaWxlbmFtZSA9IHBhc3RlMChwcm9qZWN0TmFtZSwgIiBkaW0iLCBuZGltcywgInJlcy4iLCBnc3ViKCJSTkFfc25uX3Jlcy4iLCAiIiwgbWV0YS5jb2wpLCAiLXVtYXAucG5nIiksIGhlaWdodCA9IDgwMCwgd2lkdGggPSA4MDApCgkJcGxvdChteXBsb3QpCgkJZGV2Lm9mZigpCgkJbXlwbG90IDwtIERpbVBsb3Qoc2V1cmF0Lm9iamVjdCwgCgkJCQkJCQkJCQkJZ3JvdXAuYnkgPSBtZXRhLmNvbCwKCQkJCQkJCQkJCQlyZWR1Y3Rpb24gPSAidW1hcCIsIAoJCQkJCQkJCQkJCXB0LnNpemUgPSAxLAoJCQkJCQkJCQkJCWNvbHMgPSBjb2xvci5wYWxldHRlKSArIAoJCQlmYWNldF93cmFwKG1ldGEuY29sKSArIAoJCQlnZ3RpdGxlKHBhc3RlMChwcm9qZWN0TmFtZSwgIiBkaW0iLCBuZGltcywgInJlcy4iLCBnc3ViKCJSTkFfc25uX3Jlcy4iLCAiIiwgbWV0YS5jb2wpKSkKCQkKCQlwbmcoZmlsZW5hbWUgPSBwYXN0ZTAocHJvamVjdE5hbWUsICIgZGltIiwgbmRpbXMsICJyZXMuIiwgZ3N1YigiUk5BX3Nubl9yZXMuIiwgIiIsIG1ldGEuY29sKSwgIi11bWFwX0ZhY2V0UmVzLnBuZyIpLCBoZWlnaHQgPSA4MDAsIHdpZHRoID0gODAwKQoJCXBsb3QobXlwbG90KQoJCWRldi5vZmYoKQoJCQoJfQp9CmBgYAoKCgpgYGB7cn0Kc2F2ZVJEUyhzZXVyYXQub2JqZWN0LCBmaWxlID0gcGFzdGUwKHByb2plY3ROYW1lLCAiX2RpbSIsIG5kaW1zLCAiLlJEUyIpKQpgYGAKCgojIyMgRXZhbHVhdGUgY2x1c3RlciBzdGFiaWxpdHkKYGBge3IgZmlnLmhlaWdodD01fQpjbHVzdHJlZShzZXVyYXQub2JqZWN0LCBwcmVmaXggPSAiUk5BX3Nubl9yZXMuIiwgbm9kZV9jb2xvdXIgPSAic2MzX3N0YWJpbGl0eSIpICsgCglzY2FsZV9jb2xvcl9jb250aW51b3VzKGxvdyA9ICdyZWQzJywgaGlnaCA9ICd3aGl0ZScpCnBuZyhmaWxlbmFtZSA9IHBhc3RlMChwcm9qZWN0TmFtZSwgIl9kaW0iLCBuZGltcywgIi1jbHVzdHJlZS5wbmciKSwgaGVpZ2h0ID0gODAwLCB3aWR0aCA9IDE2MDApCmNsdXN0cmVlKHNldXJhdC5vYmplY3QsIHByZWZpeCA9ICJSTkFfc25uX3Jlcy4iLCBub2RlX2NvbG91ciA9ICJzYzNfc3RhYmlsaXR5IikgKyAKCXNjYWxlX2NvbG9yX2NvbnRpbnVvdXMobG93ID0gJ3JlZDMnLCBoaWdoID0gJ3doaXRlJykKZGV2Lm9mZigpCmBgYAojIyBEZXNjcmliZSA5NSUgb2YgdmFyaWFuY2UgCgojIyMgTmVpZ2hib3Job29kIGFuZCB1bWFwCmBgYHtyfQp0b3QudmFyIDwtIHBlcmNlbnQudmFyaWFuY2Uoc2V1cmF0Lm9iamVjdEByZWR1Y3Rpb25zJHBjYUBzdGRldiwgcGxvdC52YXIgPSBGQUxTRSwgcmV0dXJuLnZhbCA9IFRSVUUpCm5kaW1zIDwtIGxlbmd0aCh3aGljaChjdW1zdW0odG90LnZhcikgPD0gOTUpKQoKc2V1cmF0Lm9iamVjdCA8LSBGaW5kTmVpZ2hib3JzKHNldXJhdC5vYmplY3QsIGRpbXMgPSAxOm5kaW1zKQpzZXVyYXQub2JqZWN0IDwtIEZpbmRDbHVzdGVycyhzZXVyYXQub2JqZWN0LCByZXNvbHV0aW9uID0gMC41KQpzZXVyYXQub2JqZWN0IDwtIFJ1blVNQVAoc2V1cmF0Lm9iamVjdCwgZGltcyA9IDE6IG5kaW1zKQoKYGBgClBsb3QgVU1BUAoKYGBge3J9CmZvcih4IGluIGMoMC41LCAxLCAxLjUsIDIsIDIuNSkpewoJc2V1cmF0Lm9iamVjdCA8LSBGaW5kQ2x1c3RlcnMoc2V1cmF0Lm9iamVjdCwgcmVzb2x1dGlvbiA9IHgpCn0KYGBgCgpgYGB7cn0KZm9yIChtZXRhLmNvbCBpbiBjb2xuYW1lcyhzZXVyYXQub2JqZWN0QG1ldGEuZGF0YSkpewoJaWYoZ3JlcGwocGF0dGVybiA9ICgiUk5BX3Nubl9yZXMiKSwgeCA9IG1ldGEuY29sKT09VFJVRSl7CgkJbXlwbG90IDwtIERpbVBsb3Qoc2V1cmF0Lm9iamVjdCwgCgkJCQkJCQkJCQkJZ3JvdXAuYnkgPSBtZXRhLmNvbCwKCQkJCQkJCQkJCQlyZWR1Y3Rpb24gPSAidW1hcCIsIAoJCQkJCQkJCQkJCWNvbHMgPSBjb2xvci5wYWxldHRlCgkJKSArIAoJCQlnZ3RpdGxlKHBhc3RlMChwcm9qZWN0TmFtZSwgIiBkaW0iLCBuZGltcywgInJlcyIsIGdzdWIoIlJOQV9zbm5fcmVzIiwgIiIsIG1ldGEuY29sKSApKQoJCXBsb3QobXlwbG90KQoJfQp9CmBgYAoKYGBge3J9CnNhdmVSRFMoc2V1cmF0Lm9iamVjdCwgZmlsZSA9IHBhc3RlMChwcm9qZWN0TmFtZSwgIl9kaW0iLCBuZGltcywgIi5SRFMiKSkKYGBgCgoKIyMjIEV2YWx1YXRlIGNsdXN0ZXIgc3RhYmlsaXR5CmBgYHtyIGZpZy5oZWlnaHQ9NX0KY2x1c3RyZWUoc2V1cmF0Lm9iamVjdCwgcHJlZml4ID0gIlJOQV9zbm5fcmVzLiIsIG5vZGVfY29sb3VyID0gInNjM19zdGFiaWxpdHkiKSArIAoJc2NhbGVfY29sb3JfY29udGludW91cyhsb3cgPSAncmVkMycsIGhpZ2ggPSAnd2hpdGUnKQpgYGAKCgoKCgoKCgojIEV4cG9ydCBmb3IgQmlvbWFyawojIyBJbXBvcnQgYmlvbWFyayBwcm9iZSBsaXN0CmBgYHtyfQpzZXVyYXQub2JqZWN0IDwtIHJlYWRSRFMoIkNNUFN1YnBvcF9kaW0xOC5SRFMiKQpgYGAKCmBgYHtyfQpwcm9iZS5saXN0IDwtIHJlYWQudGFibGUoKQpgYGAKCmBgYHtyfQp4cHJzbi5tdHggPC0gCmBgYAoKCiMgREdFCkkgdGhpbmsgY2x1c3RlcnMgZnJvbSA4MCUgdmFyaWFuY2UgYXQgMC41IGFuZCAxLjAgcmVzb2x1dGlvbiBhcmUgbW9zdCBzdGFibGUuIFdlJ2xsIGRvIHNvbWUgc3RhdGlzdGljcyBhbmQgREdFIG9uIHRoYXQuIFdpbGwgYWxzbyBuZWVkIHRvIGdvIGJhY2sgYW5kIHBsYXkgd2l0aCBTQ1RyYW5zZm9ybSwgc2luY2UgdGhlc2UgYXJlIG11bHRpcGxlIGNlbGwgdHlwZXMgZnJvbSBtdWx0aXBsZSBsYW5lcy4KIyMgTG9hZCBmYXZvdXJpdGUgZGltIHJlZHVjdGlvbiBmaWxlCmBgYHtyfQpyZHMuZmlsZSA8LSAiIgpzZXVyYXQub2JqZWN0IDwtIHJlYWRSRFMocmRzLmZpbGUpCm5kaW1zIDwtIGFzLm51bWVyaWMoZ3N1YigiW14wLTldIiwgIiIsIHN0cmluZ3I6OnN0cl9zcGxpdChyZHMuZmlsZSwgIl8iKVtbMV1dWzNdKSkKYGBgCgojIyBTZXQgZmF2b3VyaXRlIHJlc29sdXRpb24KYGBge3J9Cm9iamVjdC5yZXMgPC0gIi4wLjUiCklkZW50cyhzZXVyYXQub2JqZWN0KSA8LSBwYXN0ZTAoIlJOQV9zbm5fcmVzIiwgb2JqZWN0LnJlcykKbGVuZ3RoKGxldmVscyhzZXVyYXQub2JqZWN0QGFjdGl2ZS5pZGVudCkpCmBgYAoKCiMjIGNvdW50IG51bWJlciBvZiBmaWx0ZXJlZCBjZWxscyBsZWZ0IGZyb20gZWFjaCBwb3B1bGF0aW9uCmBgYHtyfQojIE51bWJlciBvZiBmaWx0ZXJlZCBjZWxscyBsZWZ0IGluIGVhY2ggcG9wCnNhcHBseShjKCJMU0ttMiIsICJDTVBtMiIsICJNRVBtIiwgIkdNUG0iKSwgZnVuY3Rpb24oeCkgKGMobnJvdyhzZXVyYXQub2JqZWN0QG1ldGEuZGF0YVtzZXVyYXQub2JqZWN0QG1ldGEuZGF0YSRvcmlnLmlkZW50ID09IHgsXSkpKSkKYGBgCgpgYGB7cn0KcGFyKG1mcm93ID0gYygyLCAyKSkKZm9yICh4IGluIGMoIkxTS20yIiwgIkNNUG0yIiwgIk1FUG0iLCAiR01QbSIpKXsKCWggPSBoaXN0KHNldXJhdC5vYmplY3RAbWV0YS5kYXRhW3NldXJhdC5vYmplY3RAbWV0YS5kYXRhJG9yaWcuaWRlbnQgPT0geCwgJ3BlcmNlbnQubXQnXSwgYnJlYWtzID0gMzAsIHBsb3QgPSBGQUxTRSkKCWgkZGVuc2l0eSA9IGgkY291bnRzL3N1bShoJGNvdW50cykqMTAwCglwbG90KGgsZnJlcT1GQUxTRSwgbWFpbiA9ICBwYXN0ZSh4LCAicGVyY2VudCBtaXRvQyIpLCB4bGFiID0gInBlcmNlbnQgbWl0b0MiLCB5bGFiID0gIkZyZXF1ZW5jeSIpCn0KcGFyKG1mcm93ID0gYygxLDEpKQpgYGAKCmBgYHtyIGZpZy53aWR0aD01fQpWbG5QbG90KHN1YnNldChzZXVyYXQub2JqZWN0LCBzdWJzZXQgPSBvcmlnLmlkZW50ID09ICJNRVBtIiksIAoJCQkJZmVhdHVyZXMgPSBjKCJuRmVhdHVyZV9STkEiLCAibkNvdW50X1JOQSIsICJwZXJjZW50Lm10IiksIG5jb2wgPSAxLCBwdC5zaXplID0gMCwgZmlsbC5ieSA9ICdpZGVudCcsIGZsaXAgPSBUUlVFKQpgYGAKCgojIyBER0Ugdy8gcmVzb2x1dGlvbiA9IDAuNQpTdHJ0IHdpdGggY29tcGFyaW5nIGFsbCBjbHVzdGVycyBhZ2FpbnN0IGFsbCBvdGhlciBjbHVzdGVycyBhbmQgd3JpdGUgb3V0IGNsdXN0ZXIgaW5mbwpjYWxjdWxhdGUgYEZpbmRBbGxNYXJrZXJzKClgIGZvciBkaWZmZXJlbnQgaWRlbnRzIGFuZCBzYXZlIHRvIG5ldyBmaWxlCmBgYHtyfQojIGlkZW50Lmxpc3QgPC0gY29sbmFtZXMoc2V1cmF0Lm9iamVjdEBtZXRhLmRhdGEpW2dyZXBsKCJeUk5BX3NubiIsIGNvbG5hbWVzKHNldXJhdC5vYmplY3RAbWV0YS5kYXRhKSldCmlkZW50Lmxpc3QgPC0gYygiUk5BX3Nubl9yZXMuMC41IiwgIlJOQV9zbm5fcmVzLjEiKQoKZm9yKHRlc3RlZC5pZGVudCBpbiBpZGVudC5saXN0KXsKCUlkZW50cyhzZXVyYXQub2JqZWN0KSA8LSB0ZXN0ZWQuaWRlbnQKCWFsbC5tYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKHNldXJhdC5vYmplY3QpCgl4bHN4Ojp3cml0ZS54bHN4KHggPSBhbGwubWFya2Vyc1ssYygiYXZnX2xvZzJGQyIsICJwX3ZhbF9hZGoiLCAiY2x1c3RlciIsICJnZW5lIildLCAKCQkJCQkJCQkJIGZpbGUgPSBwYXN0ZTAocHJvamVjdE5hbWUsICJfRmluZEFMTE1hcmtlcnNfZGltIixuZGltcywgIl9hbGxyZXMueGxzeCIpLCAKCQkJCQkJCQkJIHNoZWV0TmFtZSA9IHRlc3RlZC5pZGVudCwgCgkJCQkJCQkJCSBjb2wubmFtZXMgPSBUUlVFLCAKCQkJCQkJCQkJIHJvdy5uYW1lcyA9IEZBTFNFLCAKCQkJCQkJCQkJIGFwcGVuZCA9IFRSVUUpCn0KYGBgCgojIyBDcmVhdGUgYEZpbmRBbGxNYXJrZXJzKClgIGxpc3RzIGZvciBHU0VBCmBgYHtyfQpvYmplY3QucmVzLmFsbG1hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMoc2V1cmF0Lm9iamVjdCkKYGBgCgojIyBNYXAgSEdOQyBzeW1ib2xzCmBgYHtyfQpNb3VzZTJIdW1hblRhYmxlIDwtIE1vdXNlMkh1bWFuKG9iamVjdC5yZXMuYWxsbWFya2VycyRnZW5lKQoKSEdOQyA8LSB3aXRoKE1vdXNlMkh1bWFuVGFibGUsIE1vdXNlMkh1bWFuVGFibGUkSEdOQ1ttYXRjaChvYmplY3QucmVzLmFsbG1hcmtlcnMkZ2VuZSwgTW91c2UySHVtYW5UYWJsZSRNR0kpXSkKaGVhZChvYmplY3QucmVzLmFsbG1hcmtlcnMpCm9iamVjdC5yZXMuYWxsbWFya2VycyRIR05DIDwtIEhHTkMKdGFpbChvYmplY3QucmVzLmFsbG1hcmtlcnMpCnNpZy5yZXMgPC0gb2JqZWN0LnJlcy5hbGxtYXJrZXJzW29iamVjdC5yZXMuYWxsbWFya2VycyRwX3ZhbF9hZGogPD0gMC4wNSwgXQpzaWcucmVzIDwtIHNpZy5yZXNbYygiYXZnX2xvZzJGQyIsICJIR05DIiwgImNsdXN0ZXIiKV0Kc2lnLnJlcyA8LSBzaWcucmVzWyEoc2lnLnJlcyRIR05DID09ICIiIHwgaXMubmEoc2lnLnJlcyRIR05DKSksXSAjIEdTRUEgd2lsbCBmYWlsIGlmIHRoZXJlIGFyZSBhbnkgYmxhbmtzIG9yIE5BcyBpbiB0aGUgdGFibGUKc2lnLnJlcyA8LSBzaWcucmVzW10KCmBgYAoKCmBgYHtyfQpmb3IoY2x1c3RlciBpbiB1bmlxdWUoc2lnLnJlcyRjbHVzdGVyKSl7CglwcmludChwYXN0ZSgid3JpdGluZyBjbHVzdGVyIiwgY2x1c3RlcikpCgluZXcudGFibGUgPC0gc2lnLnJlc1tzaWcucmVzJGNsdXN0ZXIgPT0gY2x1c3RlciwgYygiSEdOQyIsICJhdmdfbG9nMkZDIildCgluZXcudGFibGUgPC0gbmV3LnRhYmxlW29yZGVyKC1uZXcudGFibGUkYXZnX2xvZzJGQyksIF0KCWRpci5jcmVhdGUocGFzdGUwKCJSYW5rTGlzdF9yZXMiLCBvYmplY3QucmVzLCAiX2ZpbmRBbGxfaGduYy8iKSwgc2hvd1dhcm5pbmdzID0gRkFMU0UpCgl3cml0ZS50YWJsZShuZXcudGFibGUsIGZpbGUgPSBwYXN0ZTAoIlJhbmtMaXN0X3JlcyIsIG9iamVjdC5yZXMsICJfZmluZEFsbF9oZ25jL3JlcyIsIG9iamVjdC5yZXMsICJjbHVzdGVyIiwgY2x1c3RlciwgIi5ybmsiKSwgcXVvdGUgPSBGQUxTRSwgcm93Lm5hbWVzID0gRkFMU0UsIGNvbC5uYW1lcyA9IFRSVUUsIHNlcCA9ICJcdCIsICkKCQp9CmBgYAoKCgpjYWxjdWxhdGUgYEZpbmRNYXJrZXJzKClgIHRoYXQgZGlzdGluZ3Vpc2ggZWFjaCBjbHVzdGVyIChtaWdodCBvdmVybGFiIGJldHdlZW4gY2x1c3RlcnMpCmBgYHtyfQojIGlkZW50Lmxpc3QgPC0gY29sbmFtZXMoc2V1cmF0Lm9iamVjdEBtZXRhLmRhdGEpW2dyZXBsKCJeUk5BX3NubiIsIGNvbG5hbWVzKHNldXJhdC5vYmplY3RAbWV0YS5kYXRhKSldCmlkZW50Lmxpc3QgPC0gYygiUk5BX3Nubl9yZXMuMC41IiwgIlJOQV9zbm5fcmVzLjEiKQpmb3IodGVzdGVkLmlkZW50IGluIGlkZW50Lmxpc3QpewoJZm9yIChjbHVzdGVyIGluIHNvcnQoYXMubnVtZXJpYyhsZXZlbHMoc2V1cmF0Lm9iamVjdEBtZXRhLmRhdGFbW3Rlc3RlZC5pZGVudF1dKSkpKXsKCQljbHVzdGVyLm1hcmtlcnMgPC0gRmluZE1hcmtlcnMoc2V1cmF0Lm9iamVjdCwgaWRlbnQuMSA9IGNsdXN0ZXIpCgkJeGxzeDo6d3JpdGUueGxzeCh4ID0gY2x1c3Rlci5tYXJrZXJzWyxjKCJhdmdfbG9nMkZDIiwgInBfdmFsX2FkaiIpXSwgCgkJCQkJCQkJCQkgZmlsZSA9IHBhc3RlMChwcm9qZWN0TmFtZSwgIl9GaW5kTWFya2Vyc19kaW0iLCBuZGltcywgZ3N1YigiUk5BX3Nubl8iLCAiIiwgdGVzdGVkLmlkZW50KSwgIi54bHN4IiksIAoJCQkJCQkJCQkJIHNoZWV0TmFtZSA9IHBhc3RlMCgiY2xzdCIsIGNsdXN0ZXIpLCAKCQkJCQkJCQkJCSBjb2wubmFtZXMgPSBUUlVFLCAKCQkJCQkJCQkJCSByb3cubmFtZXMgPSBUUlVFLCAKCQkJCQkJCQkJCSBhcHBlbmQgPSBUUlVFKQoJfQp9CmBgYAoKCgpgYGB7cn0KZm9yIChjbHVzdGVyIGluIHNvcnQoYXMubnVtZXJpYyhsZXZlbHMoc2V1cmF0Lm9iamVjdEBtZXRhLmRhdGFbcGFzdGUwKCJSTkFfc25uX3JlcyIsIG9iamVjdC5yZXMpXSkpKSl7CgljbHVzdGVyLm1hcmtlcnMgPC0gRmluZE1hcmtlcnMoc2V1cmF0Lm9iamVjdCwgaWRlbnQuMSA9IGNsdXN0ZXIpCgl4bHN4Ojp3cml0ZS54bHN4KHggPSBjbHVzdGVyLm1hcmtlcnNbLGMoImF2Z19sb2cyRkMiLCAicF92YWxfYWRqIildLCAKCQkJCQkJCQkJIGZpbGUgPSBwYXN0ZTAocHJvamVjdE5hbWUsICJfRmluZE1hcmtlcnNfZGltIiwgbmRpbXMsICJyZXMiLCBvYmplY3QucmVzLCAiLnhsc3giKSwgCgkJCQkJCQkJCSBzaGVldE5hbWUgPSBwYXN0ZTAoImNsc3QiLCBjbHVzdGVyKSwgCgkJCQkJCQkJCSBjb2wubmFtZXMgPSBUUlVFLCAKCQkJCQkJCQkJIHJvdy5uYW1lcyA9IFRSVUUsIAoJCQkJCQkJCQkgYXBwZW5kID0gVFJVRSkKfQpgYGAKCgojIE5vdGVzIG9uIGNsdXN0ZXIgc3RhYmlsaXR5CkNsdXN0ZXIgc3RhYmlsaXR5IGNvdWxkIGJlIGluZmx1ZW5jZWQgYnk6CiogY2VsbHMgaW4gZWFjaCBwb3B1bGF0aW9uIChjZWxscmFuZ2VyIHY2IGluY2x1ZGVzIG1vcmUgY2VsbHMgdGhhbiBjZWxscmFuZ2VyIHYxLCBlc3BlY2lhbGx5IGluIE1FUCkKKiBkaW1lbnNpb25hbGl0eSBpcyBpbmNvcnJlY3QKKiBTY2FsZURhdGEgZGlkbnQgYWNjb3VudCBmb3IgcmVncmVzc2lvbiBmYWN0b3JzIChlLmcuLCAibkNvdW50c19STkEiIG9yICJuRmVhdHVyZXNfUk5BIikKKiBEaWQgbm90IGNvbnNpZGVyIGNlbGwgY3ljbGUKKiBJbmNvcnJlY3Qgbm9ybWFsaXphdGlvbi9zY2FsaW5nIG1ldGhvZAoqIENsdXN0ZXJpbmcgaXMgdG9vIHN0cmljdCBvciBub3Qgc3RyaWN0IGVub3VnaAoqIG5laWdoYm9yaG9vZCBhbmFseXNpcyB1c2VkIHdyb25nIHBhcmFtZXRlcnMKKiBTaG91bGQgaW5jbHVkZSBtaXRvQyBmaWx0ZXIgKHRoZXJlJ3MgYSBjaHVuayBvZiBNRVAgdy8gbWl0b0MgQCB+NDAlKQoqIFNDVHJhbnNmb3JtIGFjY291bnRzIGJldHRlciBmb3Igc291cmNlcyBvZiB2YXJpYWJpbGl0eQo=